package cn.tm.ms.nerver.ft.support;

import java.util.concurrent.atomic.AtomicInteger;

import cn.tm.ms.nerver.common.SystemClock;
import cn.tm.ms.nerver.ft.entry.ModuleConf;
import cn.tm.ms.nerver.ft.route.IRoute;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandMetrics;

/**
 * 容错模式
 * 
 * @author lry
 *
 * @param <REQ> 请求对象
 * @param <RES> 响应对象
 */
public class FtCore<REQ, RES> extends HystrixCommand<RES> {  
	
	private REQ req;
	private ModuleConf conf;
	private IRoute<REQ, RES> route;
	private AtomicInteger retryTimes=new AtomicInteger();//计数
	
	public FtCore(Setter setter, ModuleConf conf,IRoute<REQ, RES> route, REQ req) {  
        super(setter);
        this.req=req;
        this.conf=conf;
        this.route=route;
    }  
    
	/**
	 * 依赖模块
	 */
	protected RES run() {
		try {
			return route.route(req);
		} catch (Throwable t) {
			t.printStackTrace();
			if(conf.getMaxRetryNum()>0||conf.getMaxRetryNum()==-1){//进入容错重试,-1表示无限重试
				throw new RuntimeException();
			}else{
				if(conf.isMockEnable()){//检查mock开关
					try {
						return route.mock(req);//调用mock服务进行返回
					} catch (Throwable e) {
						e.printStackTrace();
					}
				}
			}
		}
		return null;
	}
	
	/**
	 * 容错
	 */
	protected RES getFallback() {
		int retryFTNum=0;//已经重试容错次数
		while (retryFTNum<conf.getMaxRetryNum()||conf.getMaxRetryNum()==-1) {//循环进入容错重试流
			retryFTNum++;
			try {
				int times=retryTimes.addAndGet(1);//容错执行次数
				long sleepTime=route.breathCycle(times, conf.getMaxRetryNum(), conf.getRetryCycle(), SystemClock.now());//计算本次容错休眠时间
				if(sleepTime>0){
					Thread.sleep(sleepTime);
				}
				
				return route.route(req);
			} catch (Throwable t) {
				t.printStackTrace();
				if(conf.getMaxRetryNum()==-1){//进入无限循环状态
					continue;
				} else if(retryFTNum>=conf.getMaxRetryNum()){//重试完毕,则该退出了
					if(conf.isMockEnable()){//检查mock开关
						try {
							return route.mock(req);//调用mock服务进行返回
						} catch (Throwable e) {
							e.printStackTrace();
						}
					}else{
						if(conf.isFallbackFEEnable()){//所有容错失败后交易是否需要向外抛异常
							throw new RuntimeException(t);
						}
					}
					break;
				}
			}	
		}
		return null;
	}
	
	/**
	 * 指标收集
	 */
	public HystrixCommandMetrics getMetrics() {
		HystrixCommandMetrics hcMetrics=super.getMetrics();
		return hcMetrics;
	}
    
}